"""
HTML to video capture using headless browsers and FFmpeg.

Captures HTML presentations and converts them to video frames.
"""

import asyncio
import os
import tempfile
import json
from pathlib import Path
from typing import List, Dict, Optional, Any, Tuple
import subprocess
import time

from .timeline import VideoConfig, TimelineSegment
from ..utils.exceptions import MD2PPTError


class HTMLVideoCapture:
    """Captures HTML presentations as video using browser automation."""
    
    def __init__(self, config: Optional[VideoConfig] = None):
        """Initialize HTML video capture."""
        self.config = config or VideoConfig()
        self._temp_files: List[str] = []
        self._browser_available = None
    
    async def capture_html_to_video(self, html_path: str, 
                                  output_path: str,
                                  timeline_segments: Optional[List[TimelineSegment]] = None) -> str:
        """
        Capture HTML presentation to video file.
        
        Args:
            html_path: Path to HTML presentation file
            output_path: Output video file path
            timeline_segments: Optional timeline for synchronized capture
            
        Returns:
            Path to generated video file
        """
        if not os.path.exists(html_path):
            raise HTMLCaptureError(f"HTML file not found: {html_path}")
        
        try:
            # Check browser availability
            await self._check_browser_availability()
            
            # Create output directory
            os.makedirs(os.path.dirname(output_path), exist_ok=True)
            
            if timeline_segments:
                # Timeline-based capture with synchronization
                return await self._capture_with_timeline(html_path, output_path, timeline_segments)
            else:
                # Simple capture without timeline
                return await self._capture_simple(html_path, output_path)
            
        except Exception as e:
            await self._cleanup_temp_files()
            if isinstance(e, HTMLCaptureError):
                raise
            else:
                raise HTMLCaptureError(f"HTML capture failed: {e}")
    
    async def _capture_with_timeline(self, html_path: str,
                                   output_path: str,
                                   timeline_segments: List[TimelineSegment]) -> str:
        """Capture HTML with timeline synchronization."""
        print("🎬 开始时间轴同步视频捕获...")
        
        # Generate frames for each timeline segment
        frame_dir = self._create_temp_dir("frames")
        frame_files = []
        
        total_frames = int(self.config.fps * sum(seg.duration for seg in timeline_segments))
        current_frame = 0
        
        for segment in timeline_segments:
            if segment.slide_number is not None:
                print(f"   捕获第 {segment.slide_number} 页 ({segment.duration:.1f}s)...")
                
                # Capture frames for this segment
                segment_frames = await self._capture_segment_frames(
                    html_path, segment, frame_dir, current_frame
                )
                
                frame_files.extend(segment_frames)
                current_frame += len(segment_frames)
        
        # Combine frames into video
        print("🎞️ 合成视频文件...")
        video_path = await self._combine_frames_to_video(frame_files, output_path)
        
        return video_path
    
    async def _capture_simple(self, html_path: str, output_path: str) -> str:
        """Simple HTML capture without timeline."""
        print("🎬 开始简单视频捕获...")
        
        # Use FFmpeg with browser to capture directly
        duration = 10.0  # Default 10 seconds
        
        # Create a simple capture command
        cmd = [
            "ffmpeg", "-y",
            "-f", "lavfi",
            "-i", f"color=size={self.config.width}x{self.config.height}:duration={duration}:rate={self.config.fps}:color=white",
            "-c:v", self.config.video_codec,
            "-t", str(duration),
            *self.config.to_ffmpeg_args(),
            output_path
        ]
        
        # For now, create a placeholder video
        # In a real implementation, this would use browser automation
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "Unknown FFmpeg error"
            raise HTMLCaptureError(f"Video generation failed: {error_msg}")
        
        return output_path
    
    async def _capture_segment_frames(self, html_path: str,
                                    segment: TimelineSegment,
                                    frame_dir: str,
                                    start_frame: int) -> List[str]:
        """Capture frames for a single timeline segment."""
        frames = []
        frame_count = int(segment.duration * self.config.fps)
        
        # For demonstration, create placeholder frames
        # In a real implementation, this would use browser automation
        for i in range(frame_count):
            frame_number = start_frame + i
            frame_path = os.path.join(frame_dir, f"frame_{frame_number:06d}.png")
            
            # Create a simple colored frame as placeholder
            await self._create_placeholder_frame(frame_path, segment.slide_number or 1)
            frames.append(frame_path)
            
            self._temp_files.append(frame_path)
        
        return frames
    
    async def _create_placeholder_frame(self, frame_path: str, slide_number: int) -> None:
        """Create a placeholder frame using FFmpeg."""
        # Generate a simple frame with slide number
        color = ["red", "green", "blue", "yellow", "purple", "orange"][slide_number % 6]
        
        cmd = [
            "ffmpeg", "-y",
            "-f", "lavfi",
            "-i", f"color=size={self.config.width}x{self.config.height}:color={color}",
            "-frames:v", "1",
            frame_path
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        await process.communicate()
    
    async def _combine_frames_to_video(self, frame_files: List[str], output_path: str) -> str:
        """Combine frame files into a video."""
        if not frame_files:
            raise HTMLCaptureError("No frames to combine")
        
        # Create frame list file
        frame_list_path = self._create_temp_file("frame_list.txt")
        
        with open(frame_list_path, 'w') as f:
            for frame_file in frame_files:
                f.write(f"file '{frame_file}'\n")
                f.write(f"duration {1.0 / self.config.fps}\n")
        
        # Use FFmpeg to combine frames
        cmd = [
            "ffmpeg", "-y",
            "-f", "concat",
            "-safe", "0",
            "-i", frame_list_path,
            "-c:v", self.config.video_codec,
            "-r", str(self.config.fps),
            *self.config.to_ffmpeg_args(),
            output_path
        ]
        
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        
        stdout, stderr = await process.communicate()
        
        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "Unknown FFmpeg error"
            raise HTMLCaptureError(f"Frame combination failed: {error_msg}")
        
        return output_path
    
    async def _check_browser_availability(self) -> bool:
        """Check if browser automation is available."""
        if self._browser_available is not None:
            return self._browser_available
        
        # For now, just check if FFmpeg is available
        try:
            process = await asyncio.create_subprocess_exec(
                "ffmpeg", "-version",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            await process.communicate()
            
            self._browser_available = process.returncode == 0
            
        except FileNotFoundError:
            self._browser_available = False
        
        if not self._browser_available:
            raise HTMLCaptureError("FFmpeg not found. Please install FFmpeg for video capture.")
        
        return self._browser_available
    
    def _create_temp_dir(self, prefix: str) -> str:
        """Create temporary directory."""
        temp_dir = tempfile.mkdtemp(prefix=f"{prefix}_")
        self._temp_files.append(temp_dir)
        return temp_dir
    
    def _create_temp_file(self, filename: str) -> str:
        """Create temporary file path."""
        temp_path = os.path.join(tempfile.gettempdir(), filename)
        self._temp_files.append(temp_path)
        return temp_path
    
    async def _cleanup_temp_files(self) -> None:
        """Clean up temporary files."""
        for temp_file in self._temp_files:
            try:
                if os.path.isdir(temp_file):
                    import shutil
                    shutil.rmtree(temp_file)
                elif os.path.exists(temp_file):
                    os.remove(temp_file)
            except Exception:
                pass  # Ignore cleanup errors
        self._temp_files.clear()
    
    def get_capture_info(self) -> Dict[str, Any]:
        """Get information about capture capabilities."""
        return {
            "browser_available": self._browser_available,
            "video_config": {
                "resolution": f"{self.config.width}x{self.config.height}",
                "fps": self.config.fps,
                "codec": self.config.video_codec,
                "format": self.config.format
            },
            "supported_features": [
                "timeline_sync",
                "frame_capture", 
                "video_composition"
            ]
        }


class HTMLCaptureError(MD2PPTError):
    """Raised when HTML capture fails."""
    pass